home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / More Source / C⁄C++ / Xconq 7.0d37 / source / doc / texi2html < prev    next >
Text File  |  1995-02-05  |  44KB  |  1,665 lines

  1. #!/usr/unsupported/bin/perl
  2. 'di';
  3. 'ig00';
  4. #+##############################################################################
  5. #                                                                              #
  6. # File: texi2html                                                              #
  7. #                                                                              #
  8. # Description: Program to transform most Texinfo documents to HTML             #
  9. #                                                                              #
  10. #-##############################################################################
  11.  
  12. # @(#)texi2html    1.31 12/08/94    Written by Lionel Cons, Lionel.Cons@cern.ch
  13. # HACKED BY JOHN GILMORE FOR --nosplit and other stuff.  Feb95.
  14.  
  15. # The man page for this program is included at the end of this file and can be
  16. # viewed using the command 'nroff -man texi2html'.
  17. # Please read the copyright at the end of the man page.
  18.  
  19. #+++############################################################################
  20. #                                                                              #
  21. # Constants                                                                    #
  22. #                                                                              #
  23. #---############################################################################
  24.  
  25. $DEBUG_TOC = 1;
  26. $DEBUG_INDEX = 2;
  27. $DEBUG_BIB = 4;
  28. $DEBUG_GLOSS = 8;
  29. $DEBUG_DEF = 16;
  30.  
  31. $BIBRE = '\[[\w\/]+\]';            # RE for a bibliography reference
  32. $FILERE = '[\/\w.+-]+';            # RE for a file name
  33. $VARRE = '[^\s\{\}]+';            # RE for a variable name
  34. $NODERE = '[^@{}:\'`",]+';        # RE for a node name
  35. $NODESRE = '[^@{}:\'`"]+';        # RE for a list of node names
  36. $XREFRE = '[^@{}]+';            # RE for a xref (should use NODERE)
  37.  
  38. $THISPROG = "texi2html 1.31";            # program name and version
  39. $HOMEPAGE = "http://asis01.cern.ch/infohtml/texi2html.html"; # program home page
  40. $TODAY = &pretty_date;            # like "20 September 1993"
  41. $SPLITTAG = "<!-- SPLIT HERE -->\n";    # tag to know where to split
  42. $PROTECTTAG = "_ThisIsProtected_";    # tag to recognize protected sections
  43.  
  44. #
  45. # language dependent constants
  46. #
  47. $LDC_SEE = 'see';
  48. $LDC_SECTION = 'section';
  49. $LDC_IN = 'in';
  50. $LDC_TOC = 'Table of Contents';
  51. $LDC_GOTO = 'Go to the';
  52. $LDC_FOOT = 'Footnotes';
  53. # TODO: @def* shortcuts
  54.  
  55. #
  56. # texinfo section names to level
  57. #
  58. %sec2level = (
  59.           'top', 0,
  60.           'chapter', 1,
  61.           'unnumbered', 1,
  62.           'majorheading', 1,
  63.           'chapheading', 1,
  64.           'appendix', 1,
  65.           'section', 2,
  66.           'unnumberedsec', 2,
  67.           'heading', 2,
  68.           'appendixsec', 2,
  69.           'appendixsection', 2,
  70.           'subsection', 3,
  71.           'unnumberedsubsec', 3,
  72.           'subheading', 3,
  73.           'appendixsubsec', 3,
  74.           'subsubsection', 4,
  75.           'unnumberedsubsubsec', 4,
  76.           'subsubheading', 4,
  77.           'appendixsubsubsec', 4,
  78.           );
  79.  
  80. #
  81. # accent map, TeX command to ISO name
  82. #
  83. %accent_map = (
  84.            '"', 'uml',
  85.            '~', 'tilde',
  86.            '^', 'circ',
  87.            );
  88.  
  89. #
  90. # texinfo "simple things" (@foo) to HTML ones
  91. #
  92. %simple_map = (
  93.            # cf. makeinfo.c
  94.            "*", "<BR>",        # HTML+
  95.            " ", " ",
  96.            "\n", "\n",
  97.            "|", "",
  98.            # spacing commands
  99.            ":", "",
  100.            "!", "!",
  101.            "?", "?",
  102.            ".", ".",
  103.            );
  104.  
  105. #
  106. # texinfo "things" (@foo{}) to HTML ones
  107. #
  108. %things_map = (
  109.            'TeX', 'TeX',
  110.            'br', '<P>',        # paragraph break
  111.            'bullet', '*',
  112.            'copyright', '(C)',
  113.            'dots', '...',
  114.            'equiv', '==',
  115.            'error', 'error-->',
  116.            'expansion', '==>',
  117.            'minus', '-',
  118.            'point', '-!-',
  119.            'print', '-|',
  120.            'result', '=>',
  121.            'today', $TODAY,
  122.            );
  123.  
  124. #
  125. # texinfo styles (@foo{bar}) to HTML ones
  126. #
  127. %style_map = (
  128.           'asis', '',
  129.           'b', 'B',
  130.           'cite', 'CITE',
  131.           'code', 'CODE',
  132.           'ctrl', '&do_ctrl',    # special case
  133.           'dfn', 'DFN',
  134.           'dmn', '',        # useless
  135.           'emph', 'EM',
  136.           'file', '"TT',        # will put quotes, cf. &apply_style
  137.           'i', 'I',
  138.           'kbd', 'KBD',
  139.           'key', 'KBD',
  140.           'r', '',            # unsupported
  141.           'samp', '"SAMP',        # will put quotes, cf. &apply_style
  142.           'sc', '&do_sc',        # special case
  143.           'strong', 'STRONG',
  144.           't', 'TT',
  145.           'titlefont', '',        # useless
  146.           'var', 'VAR',
  147.           'w', '',            # unsupported
  148.           );
  149.  
  150. #
  151. # texinfo format (@foo/@end foo) to HTML ones
  152. #
  153. %format_map = (
  154.            'display', 'PRE',
  155.            'example', 'PRE',
  156.            'format', 'PRE',
  157.            'lisp', 'PRE',
  158.            'quotation', 'BLOCKQUOTE',
  159.            'smallexample', 'PRE',
  160.            'smalllisp', 'PRE',
  161.            # lists
  162.            'itemize', 'UL',
  163.            'enumerate', 'OL',
  164.            # poorly supported
  165.            'flushleft', 'PRE',
  166.            'flushright', 'PRE',
  167.            );
  168.  
  169. #
  170. # texinfo definition shortcuts to real ones
  171. #
  172. %def_map = (
  173.         # basic commands
  174.         'deffn', 0,
  175.         'defvr', 0,
  176.         'deftypefn', 0,
  177.         'deftypevr', 0,
  178.         'defcv', 0,
  179.         'defop', 0,
  180.         'deftp', 0,
  181.         # basic x commands
  182.         'deffnx', 0,
  183.         'defvrx', 0,
  184.         'deftypefnx', 0,
  185.         'deftypevrx', 0,
  186.         'defcvx', 0,
  187.         'defopx', 0,
  188.         'deftpx', 0,
  189.         # shortcuts
  190.         'defun', 'deffn Function',
  191.         'defmac', 'deffn Macro',
  192.         'defspec', 'deffn {Special Form}',
  193.         'defvar', 'defvr Variable',
  194.         'defopt', 'defvr {User Option}',
  195.         'deftypefun', 'deftypefn Function',
  196.         'deftypevar', 'deftypevr Variable',
  197.         'defivar', 'defcv {Instance Variable}',
  198.         'defmethod', 'defop Method',
  199.         # x shortcuts
  200.         'defunx', 'deffnx Function',
  201.         'defmacx', 'deffnx Macro',
  202.         'defspecx', 'deffnx {Special Form}',
  203.         'defvarx', 'defvrx Variable',
  204.         'defoptx', 'defvrx {User Option}',
  205.         'deftypefunx', 'deftypefnx Function',
  206.         'deftypevarx', 'deftypevrx Variable',
  207.         'defivarx', 'defcvx {Instance Variable}',
  208.         'defmethodx', 'defopx Method',
  209.         );
  210.  
  211. #
  212. # things to skip
  213. #
  214. %to_skip = (
  215.         # comments
  216.         'c', 1,
  217.         'comment', 1,
  218.         # useless
  219.         'contents', 1,
  220.         'shortcontents', 1,
  221.         'summarycontents', 1,
  222.         'footnotestyle', 1,
  223.         'end ifclear', 1,
  224.         'end ifset', 1,
  225.         'iftex', 1,
  226.         'end iftex', 1,
  227.         'titlepage', 1,
  228.         'end titlepage', 1,
  229.         # unsupported commands (formatting)
  230.         'afourpaper', 1,
  231.         'cropmarks', 1,
  232.         'finalout', 1,
  233.         'headings', 1,
  234.         'need', 1,
  235.         'page', 1,
  236.         'setchapternewpage', 1,
  237.         'everyheading', 1,
  238.         'everyfooting', 1,
  239.         'evenheading', 1,
  240.         'evenfooting', 1,
  241.         'oddheading', 1,
  242.         'oddfooting', 1,
  243.         'smallbook', 1,
  244.         'vskip', 1,
  245.         'vfill', 1,
  246.         # unsupported formats
  247.         'cartouche', 1,
  248.         'end cartouche', 1,
  249.         'group', 1,
  250.         'end group', 1,
  251.         # misc unsupported commands
  252.             'defindex', 1,
  253.         'let', 1,
  254.         );
  255.  
  256. #+++############################################################################
  257. #                                                                              #
  258. # Argument parsing, initialisation                                             #
  259. #                                                                              #
  260. #---############################################################################
  261.  
  262. $invisible_mark = '';
  263. $use_bibliography = 1;
  264. $usage = <<EOT;
  265. This is $THISPROG
  266. To convert a Texinfo file to HMTL: $0 [options] file
  267.   where options can be:
  268.     --glossary      : handle a glossary
  269.     --invisible name: use name as an invisible anchor
  270.     --menu          : handle menus
  271.     --split_chapter : split on main sections
  272.     --split_node    : split on nodes
  273.     --nosplit       : don't split at all, not even TOC
  274.     --usage         : print usage instructions
  275.     --verbose       : verbose output
  276. To check converted files: $0 -check [-verbose] files
  277. EOT
  278.  
  279. while ($_ = $ARGV[0], /^-/) {
  280.     shift;
  281.     if (/^--?d(ebug)?(\d+)?$/) { $debug = $2 || shift; next; }
  282.     if (/^--?c(heck)?$/)       { $check = 1; next; }
  283.     if (/^--?g(lossary)?$/)    { $use_glossary = 1; next; }
  284.     if (/^--?i(nvisible)?$/)   { $invisible_mark = shift; next; }
  285.     if (/^--?iso$/)            { $use_iso = 1; next; }
  286.     if (/^--?acc$/)            { $use_acc = 1; next; }
  287.     if (/^--?m(enu)?$/)        { $show_menu = 1; next; }
  288.     if (/^--?nos(plit)?$/)     { $nosplit = 1; next; }
  289.     if (/^--?s(plit)?_?(n(ode)?|c(hapter)?)?$/) {
  290.     if ($2 =~ /^n/) {
  291.         $split_node = 1;
  292.     } else {
  293.         $split_chapter = 1;
  294.     }
  295.     next;
  296.     }
  297.     if (/^--?v(erbose)?$/)     { $verbose = 1; next; }
  298.     die $usage;
  299. }
  300.  
  301. if ($check) {
  302.     die $usage unless @ARGV > 0;
  303.     ✓
  304.     exit;
  305. }
  306.  
  307. $invisible_mark = '<IMG SRC="invisible.xbm">' if $invisible_mark eq 'xbm';
  308. die $usage unless @ARGV == 1;
  309. $docu = shift(@ARGV);
  310. if ($docu =~ /.*\//) {
  311.     chop($docu_dir = $&);
  312.     $docu_name = $';
  313. } else {
  314.     $docu_dir = '.';
  315.     $docu_name = $docu;
  316. }
  317. $docu_name =~ s/\.te?x(i|info)?$//;    # basename of the document
  318.  
  319. if ($nosplit) {
  320.     $docu_toc = $docu_doc = $docu_foot = $docu_name . '.html';
  321. } else {
  322.     $docu_toc = $docu_doc = $docu_foot = $docu_name;
  323.     $docu_toc  .= '_toc.html';        # document's table of contents
  324.     $docu_doc  .= '.html';            # document's contents
  325.     $docu_foot .= '_foot.html';        # document's footnotes
  326. }
  327.  
  328. #
  329. # variables
  330. #
  331. %value = ();                # hold texinfo variables
  332. $value{'html'} = 1;            # predefine html (the output format)
  333. $value{'texi2html'} = '1.31';        # predefine texi2html (the translator)
  334. %node2sec = ();                # node to section name
  335. %node2href = ();            # node to HREF
  336. %bib2href = ();                # bibliography reference to HREF
  337. %gloss2href = ();            # glossary term to HREF
  338. @sections = ();                # list of sections
  339. %tag2pro = ();                # protected sections
  340.  
  341. #
  342. # initial indexes
  343. #
  344. $bib_num = 0;
  345. $foot_num = 0;
  346. $gloss_num = 0;
  347. $idx_num = 0;
  348. $sec_num = 0;
  349. $doc_num = 0;
  350. $html_num = 0;
  351.  
  352. #
  353. # can I use ISO8879 characters? (HTML+)
  354. #
  355. if ($use_iso) {
  356.     $things_map{'bullet'} = "•";
  357.     $things_map{'copyright'} = "©";
  358.     $things_map{'dots'} = "…";
  359.     $things_map{'equiv'} = "≡";
  360.     $things_map{'expansion'} = "→";
  361.     $things_map{'point'} = "∗";
  362.     $things_map{'result'} = "⇒";
  363. }
  364.  
  365. #
  366. # read texi2html extensions (if any)
  367. #
  368. $extensions = 'texi2html.ext'; # extensions in working directory
  369. if (-f $extensions) {
  370.     print "# reading extensions from $extensions\n" if $verbose;
  371.     require($extensions);
  372. }
  373. ($progdir = $0) =~ s/[^\/]+$//;
  374. if ($progdir && ($progdir ne './')) {
  375.     $extensions = "${progdir}texi2html.ext"; # extensions in texi2html directory
  376.     if (-f $extensions) {
  377.     print "# reading extensions from $extensions\n" if $verbose;
  378.     require($extensions);
  379.     }
  380. }
  381.  
  382. print "# reading from $docu\n" if $verbose;
  383.  
  384. #+++############################################################################
  385. #                                                                              #
  386. # Pass 1: read source, handle command, variable, simple substitution           #
  387. #                                                                              #
  388. #---############################################################################
  389.  
  390. @lines = ();                # whole document
  391. @toc_lines = ();            # table of contents
  392. $curlevel = 0;                # current level in TOC
  393. $node = '';                # current node name
  394. $in_table = 0;                # am I inside a table
  395. $table_type = '';            # type of table ('', 'f', 'v')
  396. @tables = ();                    # nested table support
  397. $in_bibliography = 0;            # am I inside a bibliography
  398. $in_glossary = 0;            # am I inside a glossary
  399. $in_top = 0;                # am I inside the top node
  400. $in_pre = 0;                # am I inside a preformatted section
  401. $in_list = 0;                # am I inside a list
  402. $in_html = 0;                # am I inside an HTML section
  403. $first_line = 1;                # is it the first line
  404. $dont_html = 0;                # don't protect HTML on this line
  405. $split_num = 0;                # split index
  406.  
  407. # build code for simple substitutions
  408. # the maps used (%simple_map and %things_map) MUST be aware of this
  409. # watch out for regexps, / and escaped characters!
  410. $subst_code = '';
  411. foreach (keys(%simple_map)) {
  412.     ($re = $_) =~ s/(\W)/\\$1/g; # protect regexp chars
  413.     $subst_code .= "s/\\\@$re/$simple_map{$_}/g && study;\n";
  414. }
  415. foreach (keys(%things_map)) {
  416.     $subst_code .= "s/\\\@$_\\{\\}/$things_map{$_}/g && study;\n";
  417. }
  418. if ($use_acc) {
  419.     # accentuated characters
  420.     foreach(keys(%accent_map)) {
  421.     $subst_code .= "s/\\\@\\$_([aeiou])/&\${1}$accent_map{$_};/g && study;\n";
  422.     }
  423. }
  424.  
  425. &init_input;
  426. while ($_ = &next_line) {
  427.     #
  428.     # remove \input on the first lines only
  429.     #
  430.     if ($first_line) {
  431.     next if /^\\input/;
  432.     $first_line = 0;
  433.     }
  434.     #
  435.     # parse texinfo tags
  436.     #
  437.     $tag = '';
  438.     $end_tag = '';
  439.     if (/^\@end\s+(\w+)\b/) {
  440.     $end_tag = $1;
  441.     } elsif (/^\@(\w+)\b/) {
  442.     $tag = $1;
  443.     }
  444.     #
  445.     # handle @ifhtml / @end ifhtml
  446.     #
  447.     if ($in_html) {
  448.     if ($end_tag eq 'ifhtml') {
  449.         $in_html = 0;
  450.     } else {
  451.         $tag2pro{$in_html} .= $_;
  452.     }
  453.     next;
  454.     } elsif ($tag eq 'ifhtml') {
  455.     $in_html = $PROTECTTAG . ++$html_num;
  456.     push(@lines, $in_html);
  457.     next;
  458.     }
  459.     #
  460.     # try to skip the line
  461.     #
  462.     if ($end_tag) {
  463.     next if $to_skip{"end $end_tag"};
  464.     } elsif ($tag) {
  465.     next if $to_skip{$tag};
  466.     last if $tag eq 'bye';
  467.     }
  468.     if ($in_top) {
  469.     # parsing the top node
  470.     if ($tag eq 'node' || $tag eq 'include' || $sec2level{$tag}) {
  471.         # no more in top
  472.         $in_top = 0;
  473.     } else {
  474.         # skip it
  475.         next;
  476.     }
  477.     }
  478.     #
  479.     # try to remove inlined comments
  480.     # syntax from tex-mode.el comment-start-skip
  481.     #
  482.     s/((^|[^\@])(\@\@)*)\@c(omment)? .*/$1/;
  483.     # non-@ substitutions cf. texinfmt.el
  484.     s/``/"/g && study;
  485.     s/''/"/g && study;
  486.     s/([\w ])---([\w ])/$1--$2/g && study;
  487.     #
  488.     # analyze the tag
  489.     #
  490.     if ($tag) {
  491.     # skip lines
  492.     &skip_until($tag), next if $tag eq 'ignore';
  493.     &skip_until($tag), next if $tag eq 'ifinfo';
  494.     &skip_until($tag), next if $tag eq 'tex';
  495.     # handle special tables
  496.     if ($tag eq 'table') {
  497.         $table_type = '';
  498.     } elsif ($tag eq 'ftable') {
  499.         $tag = 'table';
  500.         $table_type = 'f';
  501.     } elsif ($tag eq 'vtable') {
  502.         $tag = 'table';
  503.         $table_type = 'v';
  504.     }
  505.     # special cases
  506.     if ($tag eq 'top' || ($tag eq 'node' && /^\@node\s+top\s*,/i)) {
  507.         $in_top = 1;
  508.         @lines = (); # ignore all lines before top (title page garbage)
  509.         next;
  510.     } elsif ($tag eq 'node') {
  511.         $in_top = 0;
  512.         &protect_html;    # if node contains '&' for instance
  513.         warn "Bad node line: $_" unless $_ =~ /^\@node\s$NODESRE$/o;
  514.         s/^\@node\s+//;
  515.         ($node) = split(/,/);
  516.         $node =~ s/\s+/ /g; # normalize
  517.         $node =~ s/ $//;
  518.         if ($split_node) {
  519.         &next_doc;
  520.         push(@lines, $SPLITTAG) if $split_num++;
  521.         push(@sections, $node);
  522.         }
  523.         next;
  524.     } elsif ($tag eq 'include') {
  525.         if (/^\@include\s+($FILERE)\s*$/o) {
  526.         $file = $1;
  527.         $file = "$docu_dir/$file" unless -e $file;
  528.         if (-e $file) {
  529.             &open($file);
  530.             print "# including $file\n" if $verbose;
  531.         } else {
  532.             warn "Can't find $file, skipping";
  533.         }
  534.         } else {
  535.         warn "Bad include line: $_";
  536.         }
  537.         next;
  538.     } elsif ($tag eq 'ifclear') {
  539.         if (/^\@ifclear\s+($VARRE)\s*$/o) {
  540.         next unless defined($value{$1});
  541.         &skip_until($tag);
  542.         } else {
  543.         warn "Bad ifclear line: $_";
  544.         }
  545.         next;
  546.     } elsif ($tag eq 'ifset') {
  547.         if (/^\@ifset\s+($VARRE)\s*$/o) {
  548.         next if defined($value{$1});
  549.         &skip_until($tag);
  550.         } else {
  551.         warn "Bad ifset line: $_";
  552.         }
  553.         next;
  554.     } elsif ($tag eq 'menu' && ! $show_menu) {
  555.         &skip_until($tag);
  556.         next;
  557.     } elsif ($format_map{$tag}) {
  558.         $in_pre = 1 if $format_map{$tag} eq 'PRE';
  559.         $in_list++ if $format_map{$tag} eq 'UL' || $format_map{$tag} eq 'OL' ;
  560.         push(@lines, "<$format_map{$tag}>\n");
  561.         next;
  562.     } elsif ($tag eq 'table') {
  563.         if (/^\@[fv]?table\s+\@(\w+)\s*$/) {
  564.         $in_table = $1;
  565.         unshift(@tables, join($;, $table_type, $in_table));
  566.         push(@lines, "<DL COMPACT>\n");
  567.         } else {
  568.         warn "Bad table line: $_";
  569.         }
  570.         next;
  571.     } elsif ($tag eq 'synindex' || $tag eq 'syncodeindex') {
  572.         if (/^\@$tag\s+(\w)\w\s+(\w)\w\s*$/) {
  573.         eval("*${1}index = *${2}index");
  574.         } else {
  575.         warn "Bad syn*index line: $_";
  576.         }
  577.         next;
  578.     } elsif ($tag eq 'sp') {
  579.         push(@lines, "<P>\n");
  580.         next;
  581.     } elsif (defined($def_map{$tag})) {
  582.         if ($def_map{$tag}) {
  583.         s/^\@$tag\s+//;
  584.         $tag = $def_map{$tag};
  585.         $_ = "\@$tag $_";
  586.         $tag =~ s/\s.*//;
  587.         }
  588.     }
  589.     if (defined($def_map{$tag})) {
  590.         s/^\@$tag\s+//;
  591.         $tag =~ s/x$//;
  592.         1 while s/(\{[^\}]*)\s+([^\{]*\})/$1$;9$2/; # protect spaces inside {}
  593.         &protect_html;
  594.         @args = split(/\s+/, $_);
  595.         foreach(@args) {s/$;9/ /g;} # unprotect spaces
  596.         $type = shift(@args);
  597.         $type =~ s/^\{(.*)\}$/$1/;
  598.         print "# def ($tag): {$type} ", join(', ', @args), "\n"
  599.         if $debug & $DEBUG_DEF;
  600.         $type .= ':'; # it's nicer like this
  601.         $name = shift(@args);
  602.         $name =~ s/^\{(.*)\}$/$1/;
  603.         if ($tag eq 'deffn' || $tag eq 'defvr' || $tag eq 'deftp') {
  604.         $_ = "<U>$type</U> <B>$name</B>";
  605.         $_ .= " <I>@args</I>" if @args;
  606.         $_ .= "<P>\n";
  607.         } elsif ($tag eq 'deftypefn' || $tag eq 'deftypevr'
  608.              || $tag eq 'defcv' || $tag eq 'defop') {
  609.         $ftype = $name;
  610.         $name = shift(@args);
  611.         $name =~ s/^\{(.*)\}$/$1/;
  612.         $_ = "<U>$type</U> $ftype <B>$name</B>";
  613.         $_ .= " <I>@args</I>" if @args;
  614.         $_ .= "<P>\n";
  615.         } else {
  616.         warn "Unknown definition type: $tag\n";
  617.         $_ = "<U>$type</U> <B>$name</B>";
  618.         $_ .= " <I>@args</I>" if @args;
  619.         $_ .= "<P>\n";
  620.         }
  621.         if ($tag eq 'deffn' || $tag eq 'deftypefn' || $tag eq 'defop') {
  622.         unshift(@input_spool, "\@findex $name\n");
  623.         } elsif ($tag eq 'defvr' || $tag eq 'deftypevr' || $tag eq 'defcv') {
  624.         unshift(@input_spool, "\@vindex $name\n");
  625.         } else {
  626.         unshift(@input_spool, "\@tindex $name\n");
  627.         }
  628.         $dont_html = 1;
  629.     }
  630.     } elsif ($end_tag) {
  631.     if ($format_map{$end_tag}) {
  632.         $in_pre = 0 if $format_map{$end_tag} eq 'PRE';
  633.         $in_list-- if $format_map{$end_tag} eq 'UL' || $format_map{$end_tag} eq 'OL' ;
  634.         push(@lines, "</$format_map{$end_tag}>\n");
  635.     } elsif ($end_tag eq 'table' ||
  636.          $end_tag eq 'ftable' ||
  637.          $end_tag eq 'vtable') {
  638.         shift(@tables);
  639.         if (@tables) {
  640.         ($table_type, $in_table) = split($;, $tables[0]);
  641.         } else {
  642.         $in_table = 0;
  643.         }
  644.         push(@lines, "</DL>\n");
  645.     } elsif (defined($def_map{$end_tag})) {
  646.         push(@lines, "<P>\n");
  647.     } elsif ($end_tag eq 'menu') {
  648.         push(@lines, $_); # must keep it for pass 2
  649.     }
  650.     next;
  651.     }
  652.     #
  653.     # misc things
  654.     #
  655.     # protect texi and HTML things
  656.     &protect_texi;
  657.     &protect_html unless $dont_html;
  658.     $dont_html = 0;
  659.     # substitution (unsupported things)
  660.     s/^\@center\s+//g && study;
  661.     s/^\@exdent\s+//g && study;
  662.     s/\@noindent\s+//g && study;
  663.     s/\@refill\s+//g && study;
  664.     # other substitutions
  665.     eval($subst_code);
  666.     s/\@value{($VARRE)}/$value{$1}/eg;
  667.     s/\@footnote\{/\@footnote$docu_doc\{/g; # mark footnotes, cf. pass 4
  668.     #
  669.     # analyze the tag again
  670.     #
  671.     if ($tag) {
  672.     if ($sec2level{$tag} > 0) {
  673.         if (/^\@$tag\s+(.+)$/) {
  674.         $name = $1;
  675.         $name =~ s/\s+$//;
  676.         $level = $sec2level{$tag};
  677.         if ($tag =~ /heading$/) {
  678.             $_ = "<H$level>$name</H$level>\n";
  679.             print "# heading, section $name, level $level\n"
  680.             if $debug & $DEBUG_TOC;
  681.         } else {
  682.             if ($split_chapter && $level == 1) {
  683.             &next_doc;
  684.             push(@lines, $SPLITTAG) if $split_num++;
  685.             push(@sections, $name);
  686.             }
  687.             $id = 'SEC' . ++$sec_num;
  688.             # check biblio and glossary
  689.             $in_bibliography = ($name =~ /^bibliography$/i);
  690.             $in_glossary = ($name =~ /^glossary$/i);
  691.             # check node
  692.             if ($node) {
  693.             if ($node2sec{$node}) {
  694.                 warn "Duplicate node found: $node\n";
  695.             } else {
  696.                 $node2sec{$node} = $name;
  697.                 $node2href{$node} = "$docu_doc#$id";
  698.                 print "# node $node, section $name, level $level\n"
  699.                 if $debug & $DEBUG_TOC;
  700.             }
  701.             $node = '';
  702.             } else {
  703.             print "# no node, section $name, level $level\n"
  704.                 if $debug & $DEBUG_TOC;
  705.             }
  706.             # update TOC
  707.             while ($level > $curlevel) {
  708.             $curlevel++;
  709.             push(@toc_lines, "<UL>\n");
  710.             }
  711.             while ($level < $curlevel) {
  712.             $curlevel--;
  713.             push(@toc_lines, "</UL>\n");
  714.             }
  715.             $_ = "<LI>" . &anchor("TOC$sec_num", "$docu_doc#$id", $name, 1);
  716.             push(@toc_lines, &substitute_style($_));
  717.             # update DOC
  718.             $_ =  "<H$level>" . &anchor($id, "$docu_toc#TOC$sec_num", $name) . "</H$level>\n";
  719.         }
  720.         # update DOC
  721.         push(@lines, $_);
  722.         next;
  723.         } else {
  724.         warn "Bad section line: $_";
  725.         }
  726.     } else {
  727.         # track variables
  728.         $value{$1} = $2, next if /^\@set\s+($VARRE)\s+(.*)$/o;
  729.         delete $value{$1}, next if /^\@clear\s+($VARRE)\s*$/o;
  730.         # store things
  731.         $value{'filename'} = $1, next if /^\@setfilename\s+(.*)$/;
  732.         $value{'author'} .= "$1\n", next if /^\@author\s+(.*)$/;
  733.         $value{'subtitle'} .= "$1\n", next if /^\@subtitle\s+(.*)$/;
  734.         $value{'title'} = $1, next if /^\@settitle\s+(.*)$/;
  735.         $value{'title'} = $1, next if /^\@title\s+(.*)$/;
  736.         # index
  737.         if (/^\@(.index)\s+/) {
  738.         $id = 'IDX' . ++$idx_num;
  739.         $index = $1;
  740.         $what = &substitute_style($');
  741.         $what =~ s/\s+$//;
  742.         print "# found $index for '$what' id $id\n"
  743.             if $debug & $DEBUG_INDEX;
  744.         eval("\$$index\{\$what\} = \"$docu_doc#$id\"");
  745.         # check last line
  746.         if ($lines[$#lines] =~ /^\</) {
  747.             # we can safely (?) insert the index before the last line
  748.             splice(@lines, -1, 0, &anchor($id, '', $invisible_mark, 1));
  749.         } else {
  750.             # it's safer to append the index
  751.             push(@lines, &anchor($id, '', $invisible_mark, !$in_pre));
  752.         }
  753.         next;
  754.         }
  755.         # list item
  756.         if (/^\@itemx?\s+/) {
  757.         $what = $';
  758.         $what =~ s/\s+$//;
  759.         if ($in_bibliography && $use_bibliography) {
  760.             if ($what =~ /^$BIBRE$/o) {
  761.             $id = 'BIB' . ++$bib_num;
  762.             $bib2href{$what} = "$docu_doc#$id";
  763.             print "# found bibliography for '$what' id $id\n"
  764.                 if $debug & $DEBUG_BIB;
  765.             $what = &anchor($id, '', $what);
  766.             }
  767.         } elsif ($in_glossary && $use_glossary) {
  768.             $id = 'GLOSS' . ++$gloss_num;
  769.             $entry = $what;
  770.             $entry =~ tr/A-Z/a-z/ unless $entry =~ /^[A-Z\s]+$/;
  771.             $gloss2href{$entry} = "$docu_doc#$id";
  772.             print "# found glossary for '$entry' id $id\n"
  773.             if $debug & $DEBUG_GLOSS;
  774.             $what = &anchor($id, '', $what);
  775.         }
  776.         if ($in_table) {
  777.             if ($things_map{$in_table} && !$what) {
  778.             # special case to allow @table @bullet for instance
  779.             push(@lines, "<DT>$things_map{$in_table}\n<DD>");
  780.             } else {
  781.             push(@lines, "<DT>\@$in_table\{$what\}\n<DD>");
  782.             }
  783.             if ($table_type) { # add also an index
  784.             unshift(@input_spool, "\@${table_type}index $what\n");
  785.             }
  786.         } else {
  787.             push(@lines, "<LI>$what\n");
  788.         }
  789.         next;
  790.         }
  791.     }
  792.     }
  793.     # paragraph separator
  794.     $_ = "<P>\n" if $_ eq "\n" && !$in_pre && !$in_list && !$in_table;
  795.     # otherwise
  796.     push(@lines, $_);
  797. }
  798.  
  799. # Almost finish TOC -- leave it open at level 1 in case we want to
  800. # add a Footnotes entry.
  801. $level = 1;
  802. while ($level < $curlevel) {
  803.     $curlevel--;
  804.     push(@toc_lines, "</UL>\n");
  805. }
  806.  
  807. print "# end of pass 1\n" if $verbose;
  808.  
  809. #+++############################################################################
  810. #                                                                              #
  811. # Pass 2/3: handle style, menu, index, cross-reference                         #
  812. #                                                                              #
  813. #---############################################################################
  814.  
  815. @lines2 = ();                # whole document (2nd pass)
  816. @lines3 = ();                # whole document (3rd pass)
  817. $in_menu = 0;                # am I inside a menu
  818.  
  819. while (@lines) {
  820.     $_ = shift(@lines);
  821.     #
  822.     # special case (protected sections)
  823.     #
  824.     if (/^$PROTECTTAG/o) {
  825.     push(@lines2, $_);
  826.     next;
  827.     }
  828.     #
  829.     # menu
  830.     #
  831.     $in_menu = 1, push(@lines2, "<UL>\n"), next if /^\@menu\b/;
  832.     $in_menu = 0, push(@lines2, "</UL>\n"), next if /^\@end\s+menu\b/;
  833.     if ($in_menu) {
  834.     if (/^\*\s+($NODERE)::/o) {
  835.         $descr = $';
  836.         chop($descr);
  837.         &menu_entry($1, $1, $descr);
  838.     } elsif (/^\*\s+(.+):\s+([^\t,\.\n]+)[\t,\.\n]/) {
  839.         $descr = $';
  840.         chop($descr);
  841.         &menu_entry($1, $2, $descr);
  842.     } elsif (/^\*/) {
  843.         warn "Bad menu line: $_";
  844.     } else { # description continued?
  845.         push(@lines2, $_);
  846.     }
  847.     next;
  848.     }
  849.     #
  850.     # printindex
  851.     #
  852.     if (/^\@printindex\s+(\w)\w\b/) {
  853.     eval("*ary = *$1index");
  854.     @keys = keys(%ary);
  855.     foreach$key (@keys) {
  856.         $_ = $key;
  857.         1 while s/<(\w+)>\`(.*)\'<\/\1>/$2/; # remove HTML tags with quotes
  858.         1 while s/<(\w+)>(.*)<\/\1>/$2/; # remove HTML tags
  859.         &unprotect_html;
  860.         &unprotect_texi;
  861.         tr/A-Z/a-z/; # lowercase
  862.         $key2alpha{$key} = $_;
  863.         print "# index $key sorted as $_\n"
  864.         if $key ne $_ && $debug & $DEBUG_INDEX;
  865.     }
  866.     $last_letter = '';
  867.     foreach(sort(byalpha @keys)) {
  868.         $letter = substr($key2alpha{$_}, 0, 1);
  869.         $letter = substr($key2alpha{$_}, 0, 2) if $letter eq $;;
  870.         if ($letter ne $last_letter) {
  871.         local($_) = $letter;
  872.         &protect_html;
  873.         push(@lines2, "</DIR>\n") if $last_letter;
  874.         push(@lines2, "<H2>$_</H2>\n");
  875.         push(@lines2, "<DIR>\n");
  876.         $last_letter = $letter;
  877.         }
  878.         push(@lines2, "<LI>" . &anchor('', $ary{$_}, $_, 1));
  879.     }
  880.     push(@lines2, "</DIR>\n") if $last_letter;
  881.     next;
  882.     }
  883.     #
  884.     # simple style substitutions
  885.     #
  886.     $_ = &substitute_style($_);
  887.     #
  888.     # xref
  889.     #
  890.     while (/\@(x|px|info|)ref{($XREFRE)(}?)/o) {
  891.     # note: Texinfo may accept other characters
  892.     ($type, $nodes, $full) = ($1, $2, $3);
  893.     ($before, $after) = ($`, $');
  894.     if (! $full && $after) {
  895.         warn "* Bad xref (no ending } on line): $_";
  896.         $_ = "$before$;0${type}ref\{$nodes$after";
  897.         next; # while xref
  898.     }
  899.     if ($type eq 'x') {
  900.         $type = 'See ';
  901.     } elsif ($type eq 'px') {
  902.         $type = 'see ';
  903.     } elsif ($type eq 'info') {
  904.         $type = 'See Info';
  905.     } else {
  906.         $type = '';
  907.     }
  908.     unless ($full) {
  909.         $next = shift(@lines);
  910.         $next = &substitute_style($next);
  911.         chop($nodes); # remove final newline
  912.         if ($next =~ /\}/) { # splitted on 2 lines
  913.         $nodes .= " $`";
  914.         $after = $';
  915.         } else {
  916.         $nodes .= " $next";
  917.         $next = shift(@lines);
  918.         $next = &substitute_style($next);
  919.         chop($nodes);
  920.         if ($next =~ /\}/) { # splitted on 3 lines
  921.             $nodes .= " $`";
  922.             $after = $';
  923.         } else {
  924.             warn "* Bad xref (no ending }): $_";
  925.             $_ = "$before$;0xref\{$nodes$after";
  926.             unshift(@lines, $next);
  927.             next; # while xref
  928.         }
  929.         }
  930.     }
  931.     $nodes =~ s/\s+/ /g; # normalize
  932.     @args = split(/\s*,\s*/, $nodes);
  933.     $node = $args[0]; # the node is always the first arg
  934.     $sec = $node2sec{$node};
  935.     if (@args == 5) { # reference to another manual
  936.         $sec = $args[2] || $node;
  937.         $man = $args[4] || $args[3];
  938.         $_ = "${before}${type}section `$sec' in \@cite{$man}$after";
  939.     } elsif ($type =~ /Info/) { # inforef
  940.         warn "Wrong number of arguments: $_" unless @args == 3;
  941.         ($nn, $rn, $in) = @args;
  942.         $_ = "${before}${type} file `$in', node `$nn'$after";
  943.     } elsif ($sec) {
  944.         $href = $node2href{$node};
  945.         $_ = "${before}${type}section " . &anchor('', $href, $sec) . $after;
  946.     } else {
  947.         warn "Undefined node ($node): $_";
  948.         $_ = "$before$;0xref{$nodes}$after";
  949.     }
  950.     }
  951.     #
  952.     # try to guess bibliography references or glossary terms
  953.     #
  954.     unless (/^<H\d><A NAME=\"SEC\d/) {
  955.     if ($use_bibliography) {
  956.         $done = '';
  957.         while (/$BIBRE/o) {
  958.         ($pre, $what, $post) = ($`, $&, $');
  959.         $href = $bib2href{$what};
  960.         if (defined($href) && $post !~ /^[^<]*<\/A>/) {
  961.             $done .= $pre . &anchor('', $href, $what);
  962.         } else {
  963.             $done .= "$pre$what";
  964.         }
  965.         $_ = $post;
  966.         }
  967.         $_ = $done . $_;
  968.     }
  969.     if ($use_glossary) {
  970.         $done = '';
  971.         while (/\b\w+\b/) {
  972.         ($pre, $what, $post) = ($`, $&, $');
  973.         $entry = $what;
  974.         $entry =~ tr/A-Z/a-z/ unless $entry =~ /^[A-Z\s]+$/;
  975.         $href = $gloss2href{$entry};
  976.         if (defined($href) && $post !~ /^[^<]*<\/A>/) {
  977.             $done .= $pre . &anchor('', $href, $what);
  978.         } else {
  979.             $done .= "$pre$what";
  980.         }
  981.         $_ = $post;
  982.         }
  983.         $_ = $done . $_;
  984.     }
  985.     }
  986.     # otherwise
  987.     push(@lines2, $_);
  988. }
  989. print "# end of pass 2\n" if $verbose;
  990.  
  991. #
  992. # splitted style substitutions
  993. #
  994. while (@lines2) {
  995.     $_ = shift(@lines2);
  996.     #
  997.     # special case (protected sections)
  998.     #
  999.     if (/^$PROTECTTAG/o) {
  1000.     push(@lines3, $_);
  1001.     next;
  1002.     }
  1003.     #
  1004.     # splitted style substitutions
  1005.     #
  1006.     $old = '';
  1007.     while ($old ne $_) {
  1008.         $old = $_;
  1009.     if (/\@(\w+)\{/) {
  1010.         ($before, $style, $after) = ($`, $1, $');
  1011.         if (defined($style_map{$style})) {
  1012.         $_ = $after;
  1013.         $text = '';
  1014.         $after = '';
  1015.         $failed = 1;
  1016.         while (@lines2) {
  1017.             if (/\}/) {
  1018.             $text .= $`;
  1019.             $after = $';
  1020.             $failed = 0;
  1021.             last;
  1022.             } else {
  1023.             $text .= $_;
  1024.             $_ = shift(@lines2);
  1025.             }
  1026.         }
  1027.         if ($failed) {
  1028.             die "* Bad syntax (\@$style) after: $before\n";
  1029.         } else {
  1030.             $text = &apply_style($style, $text);
  1031.             $_ = "$before$text$after";
  1032.         }
  1033.         }
  1034.     }
  1035.     }
  1036.     # otherwise
  1037.     push(@lines3, $_);
  1038. }
  1039. print "# end of pass 3\n" if $verbose;
  1040.  
  1041. #+++############################################################################
  1042. #                                                                              #
  1043. # Pass 4: foot notes, final cleanup                                            #
  1044. #                                                                              #
  1045. #---############################################################################
  1046.  
  1047. @lines4 = ();                # whole document (4th pass)
  1048. @foot_lines = ();            # footnotes
  1049. @doc_lines = ();            # final document
  1050. $end_of_para = 0;            # true if last line is <P>
  1051.  
  1052. while (@lines3) {
  1053.     $_ = shift(@lines3);
  1054.     #
  1055.     # special case (protected sections)
  1056.     #
  1057.     if (/^$PROTECTTAG/o) {
  1058.     push(@doc_lines, $_);
  1059.     $end_of_para = 0;
  1060.     next;
  1061.     }
  1062.     #
  1063.     # footnotes
  1064.     #
  1065.     while (/\@footnote([^\{\s]+)\{/) {
  1066.     ($before, $d, $after) = ($`, $1, $');
  1067.     $_ = $after;
  1068.     $text = '';
  1069.     $after = '';
  1070.     $failed = 1;
  1071.     while (@lines3) {
  1072.         if (/\}/) {
  1073.         $text .= $`;
  1074.         $after = $';
  1075.         $failed = 0;
  1076.         last;
  1077.         } else {
  1078.         $text .= $_;
  1079.         $_ = shift(@lines3);
  1080.         }
  1081.     }
  1082.     if ($failed) {
  1083.         die "* Bad syntax (\@footnote) after: $before\n";
  1084.     } else {
  1085.         $id = 'FOOT' . ++$foot_num;
  1086.         $foot = "($foot_num)";
  1087.         push(@foot_lines, "<H3>" . &anchor($id, "$d#REF$foot_num", $foot) . "</H3>\n");
  1088.         push(@foot_lines, "$text\n");
  1089.         $_ = $before . &anchor("REF$foot_num", "$docu_foot#$id", $foot) . $after;
  1090.     }
  1091.     }
  1092.     #
  1093.     # remove unnecessary <P>
  1094.     #
  1095.     if ($_ eq "<P>\n") {
  1096.     next if $end_of_para++;
  1097.     } else {
  1098.     $end_of_para = 0;
  1099.     }
  1100.     # otherwise
  1101.     push(@doc_lines, $_);
  1102. }
  1103.  
  1104. # Finish up the table of contents.
  1105. if (@foot_lines) {
  1106.     push(@toc_lines, "<LI>" .  &anchor("FOOTREF", "$docu_foot#FOOTNOTES", "Footnotes") . "\n");
  1107. }
  1108. $level = 0;
  1109. while ($level < $curlevel) {
  1110.     $curlevel--;
  1111.     push(@toc_lines, "</UL>\n");
  1112. }
  1113.  
  1114. print "# end of pass 4\n" if $verbose;
  1115.  
  1116. #+++############################################################################
  1117. #                                                                              #
  1118. # Pass 5: print things                                                         #
  1119. #                                                                              #
  1120. #---############################################################################
  1121.  
  1122. $header = <<EOT;
  1123. <!-- This HTML file has been created by $THISPROG
  1124.      from $docu on $TODAY -->
  1125. EOT
  1126.  
  1127. $_ = &substitute_style($value{'title'} || "Untitled Document");
  1128. &unprotect_texi;
  1129. $title = $_;
  1130.  
  1131. #
  1132. # print TOC
  1133. #
  1134. if (open(FILE, "> $docu_toc")) {
  1135.     print "# creating $docu_toc...\n" if $verbose;
  1136.     &print_header("$title - Table of Contents");
  1137.     print FILE "<H1>$title</H1>\n";
  1138.     if ($value{'subtitle'}) {
  1139.     chop($value{'subtitle'}); # rmv last \n
  1140.     foreach (split(/\n/, $value{'subtitle'})) {
  1141.         $_ = &substitute_style($_);
  1142.         &unprotect_texi;
  1143.         print FILE "<H2>$_</H2>\n";
  1144.     }
  1145.     }
  1146.     if ($value{'author'}) {
  1147.     chop($value{'author'}); # rmv last \n
  1148.     foreach (split(/\n/, $value{'author'})) {
  1149.         $_ = &substitute_style($_);
  1150.         &unprotect_texi;
  1151.         print FILE "<ADDRESS>$_</ADDRESS>\n";
  1152.     }
  1153.     }
  1154.     print FILE "<P>\n";
  1155.     &print(*toc_lines, FILE);
  1156.     print FILE <<EOT;
  1157. <HR>
  1158. This document was generated using the
  1159. <A HREF=\"$HOMEPAGE\">texi2html</A>
  1160. translator version 1.31.
  1161. EOT
  1162.     if (!$nosplit) {
  1163.     &print_footer;
  1164.     }
  1165.     close(FILE);
  1166. } else {
  1167.     warn "Can't write to $docu_toc: $!\n";
  1168. }
  1169.  
  1170. #
  1171. # print document
  1172. #
  1173. if ($split_chapter || $split_node) {
  1174.     $doc_num = 0;
  1175.     $last_num = scalar(@sections);
  1176.     $first_doc = &doc_name(1);
  1177.     $last_doc = &doc_name($last_num);
  1178.     while (@sections) {
  1179.     $section = shift(@sections);
  1180.     &next_doc;
  1181.     if (open(FILE, "> $docu_doc")) {
  1182.         print "# creating $docu_doc...\n" if $verbose;
  1183.         &print_header("$title - $section");
  1184.         $prev_doc = ($doc_num == 1 ? undef : &doc_name($doc_num - 1));
  1185.         $next_doc = ($doc_num == $last_num ? undef : &doc_name($doc_num + 1));
  1186.         $navigation = "Go to the ";
  1187.         $navigation .= ($prev_doc ? &anchor('', $first_doc, "first") : "first");
  1188.         $navigation .= ", ";
  1189.         $navigation .= ($prev_doc ? &anchor('', $prev_doc, "previous") : "previous");
  1190.         $navigation .= ", ";
  1191.         $navigation .= ($next_doc ? &anchor('', $next_doc, "next") : "next");
  1192.         $navigation .= ", ";
  1193.         $navigation .= ($next_doc ? &anchor('', $last_doc, "last") : "last");
  1194.         $navigation .= " section, " . &anchor('', $docu_toc, "table of contents") . ".\n";
  1195.         print FILE "$navigation<HR>\n";
  1196.         # find corresponding lines
  1197.             @tmp_lines = ();
  1198.             while (@doc_lines) {
  1199.         $_ = shift(@doc_lines);
  1200.         last if ($_ eq $SPLITTAG);
  1201.         push(@tmp_lines, $_);
  1202.         }
  1203.             &print(*tmp_lines, FILE);
  1204.         print FILE "<HR>\n$navigation";
  1205.         &print_footer;
  1206.         close(FILE);
  1207.     } else {
  1208.         warn "Can't write to $docu_doc: $!\n";
  1209.     }
  1210.     }
  1211. } else {
  1212.     if ($nosplit) {
  1213.     if (open(FILE, ">> $docu_doc")) {
  1214.         print "# appending body to $docu_doc...\n" if $verbose;
  1215.         &print(*doc_lines, FILE);
  1216.         if (!@foot_lines) {
  1217.         &print_footer;
  1218.         }
  1219.         close(FILE);
  1220.     } else {
  1221.         warn "Can't append body to $docu_doc: $!\n";
  1222.     }
  1223.     } else {
  1224.     if (open(FILE, "> $docu_doc")) {
  1225.         print "# creating $docu_doc...\n" if $verbose;
  1226.         &print_header($title);
  1227.         print FILE "<H1>$title</H1>\n";
  1228.         &print(*doc_lines, FILE);
  1229.         &print_footer;
  1230.         close(FILE);
  1231.     } else {
  1232.         warn "Can't write to $docu_doc: $!\n";
  1233.     }
  1234.     }
  1235. }
  1236.  
  1237. #
  1238. # print footnotes
  1239. #
  1240. if (@foot_lines) {
  1241.     if ($nosplit) {
  1242.     if (open(FILE, ">> $docu_foot")) {
  1243.         print "# appending footnotes to $docu_foot...\n" if $verbose;
  1244.         print FILE "<H1>" . &anchor("FOOTNOTES", "$docu_toc#FOOTREF", "Footnotes") . "</H1>\n";
  1245.         &print(*foot_lines, FILE);
  1246.         &print_footer;
  1247.         close(FILE);
  1248.     } else {
  1249.         warn "Can't append footnotes to $docu_foot: $!\n";
  1250.     }
  1251.     } else {
  1252.     if (open(FILE, "> $docu_foot")) {
  1253.         print "# creating $docu_foot...\n" if $verbose;
  1254.         &print_header("$title - Footnotes");
  1255.         print FILE "<H1>" . &anchor("FOOTNOTES", "$docu_toc#FOOTREF", "$title - Footnotes") . "</H1>\n";
  1256.         &print(*foot_lines, FILE);
  1257.         &print_footer;
  1258.         close(FILE);
  1259.     } else {
  1260.         warn "Can't write to $docu_foot: $!\n";
  1261.     }
  1262.     }
  1263. }
  1264.  
  1265. print "# that's all folks\n" if $verbose;
  1266.  
  1267. #+++############################################################################
  1268. #                                                                              #
  1269. # Low level functions                                                          #
  1270. #                                                                              #
  1271. #---############################################################################
  1272.  
  1273. sub check {
  1274.     local($_, %seen, %context, $before, $match, $after);
  1275.  
  1276.     while (<>) {
  1277.     if (/\@(\*|\.|\:|\@|\{|\})/) {
  1278.         $seen{$&}++;
  1279.         $context{$&} .= "> $_" if $verbose;
  1280.         $_ = "$`XX$'";
  1281.         redo;
  1282.     }
  1283.     if (/\@(\w+)/) {
  1284.         ($before, $match, $after) = ($`, $&, $');
  1285.         if ($before =~ /\b[\w-]+$/ && $after =~ /^[\w-.]*\b/) { # e-mail address
  1286.         $seen{'e-mail address'}++;
  1287.         $context{'e-mail address'} .= "> $_" if $verbose;
  1288.         } else {
  1289.         $seen{$match}++;
  1290.         $context{$match} .= "> $_" if $verbose;
  1291.         }
  1292.         $match =~ s/^\@/X/;
  1293.         $_ = "$before$match$after";
  1294.         redo;
  1295.     }
  1296.     }
  1297.     
  1298.     foreach(sort(keys(%seen))) {
  1299.     if ($verbose) {
  1300.         print "$_\n";
  1301.         print $context{$_};
  1302.     } else {
  1303.         print "$_ ($seen{$_})\n";
  1304.     }
  1305.     }
  1306. }
  1307.  
  1308. sub open {
  1309.     local($name) = @_;
  1310.  
  1311.     ++$fh_name;
  1312.     if (open($fh_name, $name)) {
  1313.     unshift(@fhs, $fh_name);
  1314.     } else {
  1315.     warn "Can't read file $name: $!\n";
  1316.     }
  1317. }
  1318.  
  1319. sub init_input {
  1320.     @fhs = ();            # hold the file handles to read
  1321.     @input_spool = ();        # spooled lines to read
  1322.     $fh_name = 'FH000';
  1323.     &open($docu);
  1324. }
  1325.  
  1326. sub next_line {
  1327.     local($fh, $line);
  1328.  
  1329.     if (@input_spool) {
  1330.     $line = shift(@input_spool);
  1331.     return($line);
  1332.     }
  1333.     while (@fhs) {
  1334.     $fh = $fhs[0];
  1335.     $line = <$fh>;
  1336.     return($line) if $line;
  1337.     close($fh);
  1338.     shift(@fhs);
  1339.     }
  1340.     return(undef);
  1341. }
  1342.  
  1343. # used in pass 1, use &next_line
  1344. sub skip_until {
  1345.     local($tag) = @_;
  1346.     local($_);
  1347.  
  1348.     while ($_ = &next_line) {
  1349.     return if /^\@end\s+$tag\s*$/;
  1350.     }
  1351.     die "* Failed to find '$tag' after: " . $lines[$#lines];
  1352. }
  1353.  
  1354. sub menu_entry {
  1355.     local($entry, $node, $descr) = @_;
  1356.     local($href);
  1357.  
  1358.     $href = $node2href{$node};
  1359.     if ($href) {
  1360.     $descr =~ s/^\s+//;
  1361.     $descr = ": $descr" if $descr;
  1362.     push(@lines2, "<LI>" . &anchor('', $href, $entry) . "$descr\n");
  1363.     } else {
  1364.     warn "Undefined node ($node): $_";
  1365.     }
  1366. }
  1367.  
  1368. sub do_ctrl { "^$_[0]" }
  1369.  
  1370. sub do_sc { "\U$_[0]\E" }
  1371.  
  1372. sub apply_style {
  1373.     local($texi_style, $text) = @_;
  1374.     local($style);
  1375.  
  1376.     $style = $style_map{$texi_style};
  1377.     if (defined($style)) { # known style
  1378.     if ($style =~ /^\"/) { # add quotes
  1379.         $style = $';
  1380.         $text = "\`$text\'";
  1381.     }
  1382.     if ($style =~ /^\&/) { # custom
  1383.         $style = $';
  1384.         $text = &$style($text);
  1385.     } elsif ($style) { # good style
  1386.         $text = "<$style>$text</$style>";
  1387.     } else { # no style
  1388.     }
  1389.     } else { # unknown style
  1390.     $text = undef;
  1391.     }
  1392.     return($text);
  1393. }
  1394.  
  1395. sub substitute_style {
  1396.     local($_) = @_;
  1397.     local($changed, $done, $style, $text);
  1398.  
  1399.     $changed = 1;
  1400.     while ($changed) {
  1401.     $changed = 0;
  1402.     $done = '';
  1403.     while (/\@(\w+){([^\{\}]+)}/) {
  1404.         $text = &apply_style($1, $2);
  1405.         if ($text) {
  1406.         $_ = "$`$text$'";
  1407.         $changed = 1;
  1408.         } else {
  1409.         $done .= "$`\@$1";
  1410.         $_ = "{$2}$'";
  1411.         }
  1412.     }
  1413.         $_ = $done . $_;
  1414.     }
  1415.     return($_);
  1416. }
  1417.  
  1418. sub anchor {
  1419.     local($name, $href, $text, $newline) = @_;
  1420.     local($result);
  1421.  
  1422.     $result = "<A";
  1423.     $result .= " NAME=\"$name\"" if $name;
  1424.     $result .= " HREF=\"$href\"" if $href;
  1425.     $result .= ">$text</A>";
  1426.     $result .= "\n" if $newline;
  1427.     return($result);
  1428. }
  1429.  
  1430. sub pretty_date {
  1431.     local(@MoY, $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst);
  1432.  
  1433.     @MoY = ('January', 'Febuary', 'March', 'April', 'May', 'June',
  1434.         'July', 'August', 'September', 'October', 'November', 'December');
  1435.     ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime(time);
  1436.     $year += ($year < 70) ? 2000 : 1900;
  1437.     return("$mday $MoY[$mon] $year");
  1438. }
  1439.  
  1440. sub doc_name {
  1441.     local($num) = @_;
  1442.  
  1443.     return("${docu_name}_$num.html");
  1444. }
  1445.  
  1446. sub next_doc {
  1447.     $docu_doc = &doc_name(++$doc_num);
  1448. }
  1449.  
  1450. sub print {
  1451.     local(*lines, $fh) = @_;
  1452.     local($_);
  1453.  
  1454.     while (@lines) {
  1455.     $_ = shift(@lines);
  1456.     if (/^$PROTECTTAG/o) {
  1457.         $_ = $tag2pro{$_};
  1458.     } else {
  1459.         &unprotect_texi;
  1460.     }
  1461.     print $fh $_;
  1462.     }
  1463. }
  1464.  
  1465. sub print_header {
  1466.     print FILE <<EOT;
  1467. <HTML>
  1468. <HEAD>
  1469. $header
  1470. <TITLE>$_[0]</TITLE>
  1471. </HEAD>
  1472. <BODY>
  1473. EOT
  1474. }
  1475.  
  1476. sub print_footer {
  1477.     print FILE <<EOT;
  1478. </BODY>
  1479. </HTML>
  1480. EOT
  1481. }
  1482.  
  1483. sub protect_texi {
  1484.     # protect @ { } ` '
  1485.     s/\@\@/$;0/go && study;
  1486.     s/\@\{/$;1/go && study;
  1487.     s/\@\}/$;2/go && study;
  1488.     s/\@\`/$;3/go && study;
  1489.     s/\@\'/$;4/go && study;
  1490. }
  1491.  
  1492. sub protect_html {
  1493.     # protect & < >
  1494.     s/\&/\&\#38;/g && study;
  1495.     s/\</\&\#60;/g && study;
  1496.     s/\>/\&\#62;/g && study;
  1497.     s/\&\#60;\/A\&\#62;/<\/A>/g && study; # assume </A> is HTML
  1498.     s/\&\#60;A ([^\&]+)\&\#62;/<A $1>/g && study; # assume <A [^&]+> is HTML
  1499. }
  1500.  
  1501. # we should use this:
  1502. #
  1503. #  sub unprotect_texi {
  1504. #      s/$;0/\@/go && study;
  1505. #      s/$;1/\{/go && study;
  1506. #      s/$;2/\}/go && study;
  1507. #      s/$;3/\`/go && study;
  1508. #      s/$;4/\'/go && study;
  1509. #  }
  1510. #  
  1511. #  sub unprotect_html {
  1512. #      s/\&\#38;/\&/g && study;
  1513. #      s/\&\#60;/\</g && study;
  1514. #      s/\&\#62;/\>/g && study;
  1515. #  }
  1516. #
  1517. # but Perl 4.036 bugs on it (Perl 5 is OK)
  1518.  
  1519. sub unprotect_texi {
  1520.     s/$;0/\@/go;
  1521.     s/$;1/\{/go;
  1522.     s/$;2/\}/go;
  1523.     s/$;3/\`/go;
  1524.     s/$;4/\'/go;
  1525. }
  1526.  
  1527. sub unprotect_html {
  1528.     s/\&\#38;/\&/g;
  1529.     s/\&\#60;/\</g;
  1530.     s/\&\#62;/\>/g;
  1531. }
  1532.  
  1533. sub byalpha {
  1534.     $key2alpha{$a} cmp $key2alpha{$b};
  1535. }
  1536.  
  1537. ##############################################################################
  1538.  
  1539.     # These next few lines are legal in both Perl and nroff.
  1540.  
  1541. .00;            # finish .ig
  1542.  
  1543. 'di            \" finish diversion--previous line must be blank
  1544. .nr nl 0-1        \" fake up transition to first page again
  1545. .nr % 0            \" start at page 1
  1546. '; __END__ ############# From here on it's a standard manual page ############
  1547. .TH TEXI2HTML 1 "12/08/94"
  1548. .AT 3
  1549. .SH NAME
  1550. texi2html \- a Texinfo to HTML converter
  1551. .SH SYNOPSIS
  1552. .B texi2html [options] file
  1553. .PP
  1554. .B texi2html -check [-verbose] files
  1555. .SH DESCRIPTION
  1556. .I Texi2html
  1557. converts the given Texinfo file to a set of HTML files. It tries to handle
  1558. most of Texinfo commands. It creates hypertext links for cross-references,
  1559. footnotes...
  1560. .PP
  1561. It also tries to add links from a reference to its corresponding entry in the
  1562. bibliography (if any). It may also handle a glossary (see the
  1563. .B \-glossary
  1564. option).
  1565. .PP
  1566. .I Texi2html
  1567. creates several files depending on the contents of the Texinfo file and on
  1568. the chosen options (see FILES).
  1569. .PP
  1570. The HTML files created by
  1571. .I texi2html
  1572. are closer to TeX than to Info, what's why
  1573. .I texi2html
  1574. convert @iftex sections and not @ifinfo ones.
  1575. .SH OPTIONS
  1576. .TP 12
  1577. .B \-check
  1578. Check the given file and give the list of all things that may be Texinfo commands.
  1579. This may be used to check the output of
  1580. .I texi2html
  1581. to find the Texinfo commands that have been left in the HTML file.
  1582. .TP
  1583. .B \-glossary
  1584. Use the section named 'Glossary' to build a list of terms and put links in the HTML
  1585. document from each term toward its definition.
  1586. .TP
  1587. .B \-invisible \fIname\fP
  1588. Use \fIname\fP to create invisible destination anchors for index links. This is a workaround
  1589. for a known bug of many WWW browsers, including xmosaic.
  1590. .TP
  1591. .B \-menu
  1592. Show the Texinfo menus; by default they are ignored.
  1593. .TP
  1594. .B \-split_chapter
  1595. Split the output into several HTML files (one per main section:
  1596. chapter, appendix...).
  1597. .TP
  1598. .B \-split_node
  1599. Split the output into several HTML files (one per node).
  1600. .TP
  1601. .B \-usage
  1602. Print usage instructions, listing the current available command-line options.
  1603. .TP
  1604. .B \-verbose
  1605. Give a verbose output. Can be used with the
  1606. .B \-check
  1607. option.
  1608. .PP
  1609. .SH FILES
  1610. By default
  1611. .I texi2html
  1612. creates the following files (foo being the name of the Texinfo file):
  1613. .TP 16
  1614. .B foo_toc.html
  1615. The table of contents.
  1616. .TP
  1617. .B foo.html
  1618. The document's contents.
  1619. .TP
  1620. .B foo_foot.html
  1621. The footnotes (if any).
  1622. .PP
  1623. When used with the
  1624. .B \-split
  1625. option, it creates several files (one per chapter or node), named
  1626. .B foo_n.html
  1627. (n being the indice of the chapter or node), instead of the single
  1628. .B foo.html
  1629. file.
  1630. .SH VARIABLES
  1631. .I texi2html
  1632. predefines the following variables: \fBhtml\fP, \fBtexi2html\fP.
  1633. .SH ADDITIONAL COMMANDS
  1634. .I texi2html
  1635. implements the following non-Texinfo commands:
  1636. .TP 16
  1637. .B @ifhtml
  1638. This indicates the start of an HTML section, this section will passed through
  1639. without any modofication.
  1640. .TP
  1641. .B @end ifhtml
  1642. This indcates the end of an HTML section.
  1643. .SH VERSION
  1644. This is \fItexi2html\fP version 1.31, 12/08/94.
  1645. .PP
  1646. The latest version of \fItexi2html\fP can be found in WWW, cf. URL
  1647. http://asis01.cern.ch/infohtml/texi2html.html
  1648. .SH AUTHOR
  1649. Lionel Cons, CERN CN/DCI/UWS, Lionel.Cons@cern.ch
  1650. .SH COPYRIGHT
  1651. This program is the intellectual property of the European
  1652. Laboratory for Particle Physics (known as CERN). No guarantee whatsoever is
  1653. provided by CERN. No liability whatsoever is accepted for any loss or damage
  1654. of any kind resulting from any defect or inaccuracy in this information or
  1655. code.
  1656. .PP
  1657. CERN, 1211 Geneva 23, Switzerland
  1658. .SH "SEE ALSO"
  1659. GNU Texinfo Documentation Format,
  1660. HyperText Markup Language (HTML),
  1661. World Wide Web (WWW).
  1662. .SH BUGS
  1663. This program does not understand all Texinfo commands (yet).
  1664. .ex
  1665.